#include "DrawableDataPool.hpp"

#define ANIMATION_DATA_SIZE_IN_FILE 32

#include <cstring>
//DEBUG
#include <iostream>
#include "../System.hpp"
#include "AnimationData.hpp"


namespace se{
namespace Graphics{

/********************************************//**
 * \brief Creates a DrawableDataPool.
 *
 * You must then set the file from which to load it,
 * or it will be uninitialized and return only the empty animation
 * (you know, the rose one ?).
 ***********************************************/
DrawableDataPool::DrawableDataPool():
	m_DrawableData(0),
	m_contentFile(NULL),
	m_animationsFile(),
	m_dynamicAnimationLoadingEnabled(false){
	m_DrawableData.push_back(new AnimationData());// The empty animation
}

/********************************************//**
 * \brief Creates a DrawableDataPool, and assign it a file.
 *
 * \param c The ContentFile containing the animations list, and the images.
 * \param f The animation list file.
 ***********************************************/

DrawableDataPool::DrawableDataPool(ContentFile &c, uint32_t f) :
	m_DrawableData(0),
	m_contentFile(NULL),
	m_animationsFile(),
	m_dynamicAnimationLoadingEnabled(false) {
	m_DrawableData.push_back(new AnimationData());
	SetContentFile(c,f);
}

/********************************************//**
 * \brief Set the animation list.
 *
 * \param c The ContentFile containing the animations list, and the images.
 * \param f The animation list file.
 ***********************************************/
void DrawableDataPool::SetContentFile(ContentFile &c, uint32_t f){
	m_contentFile = &c;
	c.Get(m_animationsFile, f);
	m_dynamicAnimationLoadingEnabled = true;
}

/********************************************//**
 * \brief Free the file.
 *
 * From this point, a non loaded animation won't be loaded.
 * It will return not found if it is asked.
 * 
 * The object will become way more efficient, because no file
 * lookup is needed.
 *
 * The behaviour of the image loading depends on what is done
 * with the ContentFile afterwards : each AnimationData keeps a pointer
 * to the ContentFile.
 *
 ***********************************************/
inline void DrawableDataPool::RemoveContentFile(){
	m_contentFile = NULL;
	m_animationsFile.Unload();
	m_dynamicAnimationLoadingEnabled = false;
}

/********************************************//**
 * \brief Gets wether the animation is present in the currently loaded animations.
 *
 * \param s The animation ID string.
 * \return \c true if the animation was found.
 *
 ***********************************************/
inline bool DrawableDataPool::IsLoaded(const std::string& s){
	return GetPos(s)!=-1;
}

/********************************************//**
 * \brief Returns the \c AnimationData having this ID
 *
 * If not found in animations currently loaded, attempt to load it.
 * If not found in the file, returns empty animation.
 * \param s The AnimationData ID
 * \return \c empty animation if not found, or a pointer to the 
 * AnimationData if found.
 * \see DrawableDataPool::GetPtr(const std::string& s)
 ***********************************************/
AnimationData* DrawableDataPool::Get(const std::string& s){
	AnimationData* ret = GetPtr(s);
	if(!ret) ret = LoadAnim(s);
	return ret;
}

/********************************************//**
 * \brief Returns the \c AnimationData having this ID
 *
 * \param s The animation ID string
 * \return The adress to the animation, or NULL if not found in the loaded ones.
 * \see DrawableDataPool::Get(const std::string& s)
 ***********************************************/
AnimationData* DrawableDataPool::GetPtr(const std::string& s){
	int32_t pos = GetPos(s);
	return pos==-1 ? NULL : m_DrawableData[pos];
}

AnimationData* DrawableDataPool::GetDefault(){
	return m_DrawableData[0];
}

/********************************************//**
 * \brief Returns the animation position in the array.
 *
 * \return -1 if not found in the currently loaded images.
 ***********************************************/
int32_t DrawableDataPool::GetPos(const std::string& s){
	int32_t pos = -1;
	for(uint32_t i=0; i<m_DrawableData.size(); i++){
		if(m_DrawableData[i]->ID==s){
			pos = i;
			break;
		}
	}
	return pos;
}

/********************************************//**
 * \brief Load an animation from the animation list file.
 *
 * \param s The animation ID string
 * \return The adress to the loaded animation, or the empty animation if not found.
 ***********************************************/
AnimationData* DrawableDataPool::LoadAnim(const std::string& s){
	if(!m_dynamicAnimationLoadingEnabled){// Should have been loaded before.
		SE_LOG(LOAD_FAILED, "The animation \""<<s<<"\" was not found in the listAnim")
		return GetDefault();
	}
	if(s.size()+1>ANIMATION_DATA_SIZE_IN_FILE-8){
		SE_LOG(LOAD_FAILED, "DrawableDataPool::LoadAnim(\""<<s<<"\") failed. the string is too long, and coudn't fit into a animation file.");
		return GetDefault();
	}
	
	char* file = m_animationsFile.GetContentPtr();
	const char* animName = s.c_str();
	uint16_t animNameSize = (uint16_t)(s.size()+1);
	uint32_t nAnimations = (uint32_t)(m_animationsFile.GetSize()/ANIMATION_DATA_SIZE_IN_FILE);
	int16_t found = 0;
	uint32_t i = 0;
	for(i=0; i<nAnimations; ++i){
		found = !std::memcmp((void*)(file+i*ANIMATION_DATA_SIZE_IN_FILE+8),animName,animNameSize);
		if(found) break;
	}
	if(!found){
		SE_LOG(LOAD_FAILED, "The animation \""<<s<<"\" was not found in the listAnim")
		return GetDefault();// TODO : set a flag on load fail.
	}
	m_animationsFile.Seek(i*ANIMATION_DATA_SIZE_IN_FILE);
	
	uint32_t imageRef;
	uint16_t nFrames;
	uint16_t frameTime;
	m_animationsFile.Read(&nFrames,2);
	m_animationsFile.Read(&frameTime,2);
	m_animationsFile.Read(&imageRef,4);
	
	AnimationData* ret = new AnimationData(nFrames,frameTime,imageRef, *m_contentFile, s);
	m_DrawableData.push_back(ret);
	
	return ret;
}

/********************************************//**
 * \brief Unload all animations.
 ***********************************************/
void DrawableDataPool::CleanUp(){
	while(!m_DrawableData.empty()){
		delete m_DrawableData.back();
		m_DrawableData.pop_back();
	}
}

/********************************************//**
 * \brief Delete the DrawableDataPool, and free its contents.
 ***********************************************/
DrawableDataPool::~DrawableDataPool(){
	RemoveContentFile();
	CleanUp();
}


}//</namespace Graphics>
}//</namespace se>
